import { Context, Next } from 'koa';
import { config } from '../config';

/**
 * 自定义错误类
 */
export class AppError extends Error {
  public status: number;
  public code: string;
  public isOperational: boolean;

  constructor(message: string, status: number = 500, code: string = 'INTERNAL_ERROR') {
    super(message);
    this.status = status;
    this.code = code;
    this.isOperational = true;
    
    Error.captureStackTrace(this, this.constructor);
  }
}

/**
 * 常见错误码定义
 */
export const ErrorCodes = {
  // 客户端错误 4xx
  BAD_REQUEST: { status: 400, code: 'BAD_REQUEST' },
  UNAUTHORIZED: { status: 401, code: 'UNAUTHORIZED' },
  FORBIDDEN: { status: 403, code: 'FORBIDDEN' },
  NOT_FOUND: { status: 404, code: 'NOT_FOUND' },
  METHOD_NOT_ALLOWED: { status: 405, code: 'METHOD_NOT_ALLOWED' },
  CONFLICT: { status: 409, code: 'CONFLICT' },
  VALIDATION_ERROR: { status: 422, code: 'VALIDATION_ERROR' },
  TOO_MANY_REQUESTS: { status: 429, code: 'TOO_MANY_REQUESTS' },
  
  // 服务器错误 5xx
  INTERNAL_ERROR: { status: 500, code: 'INTERNAL_ERROR' },
  NOT_IMPLEMENTED: { status: 501, code: 'NOT_IMPLEMENTED' },
  BAD_GATEWAY: { status: 502, code: 'BAD_GATEWAY' },
  SERVICE_UNAVAILABLE: { status: 503, code: 'SERVICE_UNAVAILABLE' },
  GATEWAY_TIMEOUT: { status: 504, code: 'GATEWAY_TIMEOUT' },
  
  // 业务错误
  USER_NOT_FOUND: { status: 404, code: 'USER_NOT_FOUND' },
  USER_ALREADY_EXISTS: { status: 409, code: 'USER_ALREADY_EXISTS' },
  INVALID_CREDENTIALS: { status: 401, code: 'INVALID_CREDENTIALS' },
  TOKEN_EXPIRED: { status: 401, code: 'TOKEN_EXPIRED' },
  INVALID_TOKEN: { status: 401, code: 'INVALID_TOKEN' },
  PERMISSION_DENIED: { status: 403, code: 'PERMISSION_DENIED' },
  DATABASE_ERROR: { status: 500, code: 'DATABASE_ERROR' },
  EXTERNAL_SERVICE_ERROR: { status: 502, code: 'EXTERNAL_SERVICE_ERROR' }
} as const;

/**
 * 错误处理中间件
 */
export const errorHandler = () => {
  return async (ctx: Context, next: Next) => {
    try {
      await next();
    } catch (error: any) {
      // 记录错误日志
      console.error('Request error:', {
        url: ctx.url,
        method: ctx.method,
        error: error.message,
        stack: error.stack,
        timestamp: new Date().toISOString()
      });

      // 设置响应状态码
      ctx.status = error.status || 500;
      
      // 设置响应头
      ctx.type = 'application/json';
      
      // 构建错误响应
      const errorResponse: any = {
        success: false,
        error: {
          code: error.code || 'INTERNAL_ERROR',
          message: error.message || 'Internal Server Error',
          timestamp: new Date().toISOString()
        }
      };

      // 开发环境下包含堆栈信息
      if (config.nodeEnv === 'development') {
        errorResponse.error.stack = error.stack;
        errorResponse.error.details = {
          url: ctx.url,
          method: ctx.method,
          headers: ctx.headers
        };
      }

      // 特殊错误处理
      if (error.name === 'ValidationError') {
        ctx.status = 422;
        errorResponse.error.code = 'VALIDATION_ERROR';
        errorResponse.error.details = error.details || error.errors;
      } else if (error.name === 'CastError') {
        ctx.status = 400;
        errorResponse.error.code = 'BAD_REQUEST';
        errorResponse.error.message = 'Invalid data format';
      } else if (error.name === 'MongoError' || error.name === 'MongooseError') {
        ctx.status = 500;
        errorResponse.error.code = 'DATABASE_ERROR';
        errorResponse.error.message = 'Database operation failed';
      }

      ctx.body = errorResponse;
      
      // 触发应用级错误事件
      ctx.app.emit('error', error, ctx);
    }
  };
};

/**
 * 创建自定义错误的工厂函数
 */
export const createError = {
  badRequest: (message: string = 'Bad Request') => 
    new AppError(message, ErrorCodes.BAD_REQUEST.status, ErrorCodes.BAD_REQUEST.code),
  
  unauthorized: (message: string = 'Unauthorized') => 
    new AppError(message, ErrorCodes.UNAUTHORIZED.status, ErrorCodes.UNAUTHORIZED.code),
  
  forbidden: (message: string = 'Forbidden') => 
    new AppError(message, ErrorCodes.FORBIDDEN.status, ErrorCodes.FORBIDDEN.code),
  
  notFound: (message: string = 'Not Found') => 
    new AppError(message, ErrorCodes.NOT_FOUND.status, ErrorCodes.NOT_FOUND.code),
  
  conflict: (message: string = 'Conflict') => 
    new AppError(message, ErrorCodes.CONFLICT.status, ErrorCodes.CONFLICT.code),
  
  validationError: (message: string = 'Validation Error') => 
    new AppError(message, ErrorCodes.VALIDATION_ERROR.status, ErrorCodes.VALIDATION_ERROR.code),
  
  internalError: (message: string = 'Internal Server Error') => 
    new AppError(message, ErrorCodes.INTERNAL_ERROR.status, ErrorCodes.INTERNAL_ERROR.code),
  
  userNotFound: (message: string = 'User not found') => 
    new AppError(message, ErrorCodes.USER_NOT_FOUND.status, ErrorCodes.USER_NOT_FOUND.code),
  
  userAlreadyExists: (message: string = 'User already exists') => 
    new AppError(message, ErrorCodes.USER_ALREADY_EXISTS.status, ErrorCodes.USER_ALREADY_EXISTS.code),
  
  invalidCredentials: (message: string = 'Invalid credentials') => 
    new AppError(message, ErrorCodes.INVALID_CREDENTIALS.status, ErrorCodes.INVALID_CREDENTIALS.code),
  
  tokenExpired: (message: string = 'Token expired') => 
    new AppError(message, ErrorCodes.TOKEN_EXPIRED.status, ErrorCodes.TOKEN_EXPIRED.code),
  
  invalidToken: (message: string = 'Invalid token') => 
    new AppError(message, ErrorCodes.INVALID_TOKEN.status, ErrorCodes.INVALID_TOKEN.code),
  
  permissionDenied: (message: string = 'Permission denied') => 
    new AppError(message, ErrorCodes.PERMISSION_DENIED.status, ErrorCodes.PERMISSION_DENIED.code),
  
  databaseError: (message: string = 'Database error') => 
    new AppError(message, ErrorCodes.DATABASE_ERROR.status, ErrorCodes.DATABASE_ERROR.code)
};